/**
 * Behaviour startet ein CFP um die anderen Agenten zu fragen ob losgefahren werden darf.
 * Antworten werden ebenfalls hier behandelt.
 */
package de.fhin.mas.agents.navigator.behaviours;

import jade.core.AID;
import jade.core.Agent;
import jade.domain.FIPAException;
import jade.domain.FIPANames;
import jade.lang.acl.ACLMessage;
import jade.proto.ContractNetInitiator;

import java.util.Date;
import java.util.Enumeration;
import java.util.Set;
import java.util.Vector;

import de.fhin.mas.MASEvents;
import de.fhin.mas.MASServices;
import de.fhin.mas.agents.navigator.NavigatorAgent;

public class CFPMovementNecessary extends ContractNetInitiator {
	private static final long serialVersionUID = 2221351805435528947L;

	/**
	 * Anzahl der Agenten an die eine Nachricht verschickt wird Dient auch dazu
	 * um auf alle Antworten zu warten.
	 */
	private int nAgents = 0;
	private int nResponses = 0;

	public CFPMovementNecessary(Agent agent) {
		/**
		 * Aufrufen des Konstruktors der ContractNetInitiator Klasse Als Message
		 * Template wird null übergeben, daher wird auf alle eingehenden
		 * Nachrichten reagiert
		 */

		super(agent, null);
		nResponses = 0;
	}

	/**
	 * Überschreibt die prepareCfps Methode der ContractNetInitiator
	 * Basisklasse. Die Methode wird aufgerufen um die CFP Nachrichten zu
	 * erzeugen
	 */
	@Override
	protected Vector<ACLMessage> prepareCfps(ACLMessage cfp) {
		// Neue Liste, in der alle CFP Nachrichten gespeichert werden.
		Vector<ACLMessage> v = new Vector<ACLMessage>();

		/**
		 * Es wird eine neue ACL-Nachricht erzeugt. Für jeden Empfänger-Agenten
		 * muss eine eigene Nachricht erzeugt werden
		 */

		// Alle Services suchen und als Empfänger setzen
		try {
			Set<AID> all = MASServices.findAllServices(myAgent);
			for (AID currentService : all) {
				ACLMessage cfpMessage = new ACLMessage(ACLMessage.CFP);
				v.add(cfpMessage);
				cfpMessage
						.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
				cfpMessage
						.setConversationId(MASEvents.MOVEMENT_OF_PLATFORM_NECESSARY
								.getName());
				cfpMessage.setReplyByDate(new Date(
						System.currentTimeMillis() + 4000));
				cfpMessage.addReceiver(currentService);

			}
		} catch (FIPAException e) {
			// logger.log(Level.SEVERE,
			// "An exception occured while searching for other agents.", e);
		}
		// Manager schickt kein PROPOSE
		nAgents = v.size() - 1;
		((NavigatorAgent) myAgent).println(nAgents + 1
				+ " CFP-Messages prepared.");
		// List mit den CFP-Nachrichten zurückgeben
		return v;
	}

	/**
	 * This method is called when all the responses have been collected or when
	 * the timeout is expired. The used timeout is the minimum value of the slot
	 * <code>replyBy</code> of all the sent messages. By response message we
	 * intend here all the <code>propose, not-understood,
	 * refuse</code> received messages, which are not not out-of-sequence
	 * according to the protocol rules. This default implementation does
	 * nothing; programmers might wish to override the method in case they need
	 * to react to this event by analysing all the messages in just one call.
	 * 
	 * @param responses
	 *            the Vector of ACLMessage objects that have been received
	 * @param acceptances
	 *            the list of ACCEPT/REJECT_PROPOSAL to be sent back. This list
	 *            can be filled at once redefining this method, or step by step
	 *            redefining the handlePropose method.
	 **/
	@Override
	protected void handleAllResponses(Vector responses, Vector acceptances) {
		if (responses.size() < nAgents) {
			((NavigatorAgent) myAgent).println("Timeout expired: missing "
					+ (nAgents - responses.size()) + " responses");
		}
		Enumeration e = responses.elements();
		while (e.hasMoreElements()) {
			ACLMessage msg = (ACLMessage) e.nextElement();
			if (msg.getPerformative() == ACLMessage.PROPOSE) {
				ACLMessage reply = msg.createReply();
				reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
				reply
						.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
				acceptances.addElement(reply);
			}
		}
	}

	/**
	 * Liefert eine Agent eine REFUSE-Nachricht auf die cfp Anfrage zurück, so
	 * wird diese Methode mit einer Referenz auf die REFUSE Nachricht aufgerufen
	 */
	@Override
	protected void handleRefuse(ACLMessage refuse) {
		// Info ausgeben
		((NavigatorAgent) myAgent).println("REFUSE-Message from "
				+ refuse.getSender().getName()
				+ "received. Sending REJECT-PROPOSAL");

		ACLMessage reply = refuse.createReply();
		reply.setPerformative(ACLMessage.REJECT_PROPOSAL);
		reply.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
		((NavigatorAgent) myAgent).sendToAll(reply);
		((NavigatorAgent) myAgent).println("CFP Initiator wird beendet.");
		((NavigatorAgent) myAgent).cfpMovementRefuse();
		// TODO: Warte einige Zeit und starte den Vorgang dann nochmal
	}

	/**
	 * Überschreibt die HandleInform Methode der ContractNetInitiator
	 * Basisklasse. Werden Nachrichten vom Performative-Typ: INFORM erhalten, so
	 * wird diese Methode aufgerufen
	 * 
	 * @param inform
	 *            Referenz auf die INFORM-Nachricht
	 */
	@Override
	protected void handleInform(ACLMessage inform) {
		((NavigatorAgent) myAgent).println("INFORM-Message from "
				+ inform.getSender().getName() + "received.");
		nResponses++;
		/**
		 * Werden alle Informs von den Agenten empfangen kann losgefahren werden
		 */
		if (nResponses >= nAgents) {
			((NavigatorAgent) myAgent)
					.println("All INFORM-DONE Messages received");
			((NavigatorAgent) myAgent).readyToDrive();

		}
	}
}
